﻿@using Microsoft.AspNetCore.Components.Web;

@typeparam TItem

@if (Items != null)
{
    <div class="dropdown">
        <button disabled="@Disabled"
                class="btn btn-primary dropdown-toggle mr-4" data-toggle="dropdown"
                type="button" onclick="@((MouseEventArgs e) => HandleClick(e))"
                placeholder="@Placeholder"
                aria-haspopup="true" aria-expanded="false">
            @Selected
        </button>
        <CascadingValue name="Dropdown" Value="@this">
            <div class="dropdown-menu @(Show? "show":"")">
                <div class="input-group col-md-4">
                    <input class="form-control py-2 border-right-0 border"
                           type="search"
                           onsearch="@(() => RefreshDataAsync())"
                           oninput="@((ChangeEventArgs e) => UpdateFilter(e.Value))" />
                    <button class="btn btn-outline-secondary border-left-0 border" type="button"
                            onclick="@(() => RefreshDataAsync())">
                        <i class="oi oi-magnifying-glass"></i>
                    </button>
                </div>
                @foreach (TItem it in @GetItems())
                {
                    @if (ItemContent != null)
                    {
                        <a class=@(it.Equals(Value) ? "dropdown-item active" : "dropdown-item") Item="@it"
                           onclick="@(()=> HandleSelect(it, ItemContent))">
                            @ItemContent(it)
                        </a>
                    }
                }
                <!--Show pagination.-->
                @if (Pages.Count > 1 && !ShowAllData)
                {
                    <ul class="pagination justify-content-center">
                        @if (CurrentPage != 0)
                        {
                            <li onclick="@(() => SelectPage(0))"
                                style="cursor: pointer;"
                                class="page-link" href="#">&laquo;</li>
                        }
                        @foreach (int pos in Pages)
                        {
                            @if (pos >= CurrentPage - Spread && pos <= CurrentPage + Spread)
                            {
                                <li onclick="@(() => SelectPage(pos))"
                                    style="cursor: pointer;"
                                    class="page-item @(CurrentPage == pos ? "active" : null)">
                                    <span class="page-link" href="#">@(1 + pos)</span>
                                </li>
                            }

                        }
                        @if (CurrentPage != TotalPages - 1)
                        {
                            <li onclick="@(() => SelectPage(TotalPages - 1))"
                                style="cursor: pointer;"
                                class="page-link" href="#">&raquo;</li>
                        }
                    </ul>
                }
            </div>
        </CascadingValue>
    </div>
    @ChildContent
}
@code {

    /// <summary>
    /// If immediate filtering is used, the query is executed when the user presses any key.
    /// </summary>
    [Parameter]
    public bool Immediate { get; set; }

    [Parameter]
    public string? CssClass { get; set; }

    /// <summary>
    /// Is the drop down control disabled.
    /// </summary>
    [Parameter]
    public bool Disabled { get; set; }

    private List<int> Pages = new List<int>();

    /// <summary>
    /// Selected item.
    /// </summary>
    private RenderFragment? Selected;
    public TItem? _value;

    void HandleClick(MouseEventArgs args)
    {
        if (args.Button == 0)
        {
            Show = !Show;
        }
    }


    [Parameter]
    public TItem? Value
    {
        get => _value;
        set
        {
            if (_value?.GetHashCode() != value?.GetHashCode())
            {
                _value = value;
                ValueChanged.InvokeAsync(value);
            }
        }
    }
    /// <summary>
    /// Notified when value is updated..
    /// </summary>
    [Parameter]
    public EventCallback<TItem?> ValueChanged { get; set; }

    /// <summary>
    /// Is dropdown shown.
    /// </summary>
    private bool Show = false;
    [Parameter]
    public RenderFragment? ChildContent { get; set; }
    /// <summary>
    /// Gets or sets the item template for the list.
    /// </summary>
    [Parameter]
    public RenderFragment<TItem>? ItemContent { get; set; }

    private async Task UpdateFilter(object? value)
    {
        Filter = Convert.ToString(value);
        Pages.Clear();
        _currentPage = 0;
        if (Immediate)
        {
            await RefreshDataAsync();
        }
    }

    private string? Filter;
    int _pageSize = 5;

    [Parameter]
    public EventCallback<TItem> OnSelected { get; set; }

    private async Task HandleSelect(TItem item, RenderFragment<TItem> contentFragment)
    {
        Value = item;
        Selected = contentFragment.Invoke(item);
        Show = false;
        await OnSelected.InvokeAsync(item);
        StateHasChanged();
    }

    /// <summary>
    /// Page size tells how many rows are shown.
    /// </summary>
    [Parameter]
    public int PageSize
    {
        get
        {
            return _pageSize;
        }
        set
        {
            if (_pageSize < 5)
            {
                _pageSize = 5;
            }
            _pageSize = value;
        }
    }

    [Parameter]
    public string? Placeholder { get; set; }
    public delegate string? GetNameEventHandler(TItem target);
    private CancellationTokenSource? _cts;
    private int _currentPage = 0;
    /// <summary>
    /// Data from the all users is shown for the admin.
    /// </summary>
    [Parameter]
    public bool ShowAllUsers { get; set; } = true;
    /// <summary>
    /// Data from the all users is shown for the admin.
    /// </summary>
    protected bool ShowAllUserData { get; set; } = false;
    /// <summary>
    /// All table rows are shown and paginatation is not used.
    /// </summary>
    [Parameter]
    public bool ShowAllData { get; set; }
    /// <summary>
    /// Is total shown.
    /// </summary>
    [Parameter]
    public bool Total { get; set; } = true;
    /// <summary>
    /// How many page links there are before and after the selected page.
    /// </summary>
    [Parameter]
    public int Spread { get; set; } = 2;
    /// <summary>
    /// Current page.
    /// </summary>
    [Parameter]
    public int CurrentPage
    {
        get
        {
            return _currentPage;
        }
        set
        {
            if (value < 0)
            {
                //Select the first page.
                value = 0;
            }
            else if (PageSize * value > TotalCount)
            {
                //Select the last page.
                value = TotalCount / PageSize;
            }
            _currentPage = value;
        }
    }
    /// <summary>
    /// Change pagination page.
    /// </summary>
    /// <param name="index">Page index</param>
    internal async Task SelectPage(int index)
    {
        CurrentPage = index;
        await RefreshDataAsync(true);
    }
    /// <summary>
    /// The total row count.
    /// </summary>
    public int TotalCount { get; private set; }

    /// <summary>
    /// The total page count.
    /// </summary>
    public int TotalPages
    {
        get
        {
            return (int)Math.Ceiling(TotalCount / (double)PageSize);
        }
    }
    /// <summary>
    /// Gets or sets the function providing items to the list.
    /// </summary>
    [Parameter]
    public GXItemsProviderDelegate<TItem>? ItemsProvider { get; set; }
    public List<TItem> GetItems()
    {
        if (Items == null)
        {
            return new List<TItem>();
        }
        return Items.Except(HiddenItems).ToList();
    }
    /// <summary>
    /// Items in the list.
    /// </summary>
    public List<TItem>? Items { get; set; } = new List<TItem>();
    /// <summary>
    /// Hidden items is used in search.
    /// </summary>
    public List<TItem> HiddenItems { get; set; } = new List<TItem>();
    /// <summary>
    /// Read values async.
    /// </summary>
    /// <param name="renderOnSuccess">Is UI render after success operation.</param>
    public async Task RefreshDataAsync(bool renderOnSuccess = true)
    {
        _cts?.Cancel();
        _cts = new CancellationTokenSource();
        CancellationToken cancellationToken = _cts.Token;
        GXItemsProviderRequest req = new GXItemsProviderRequest(ShowAllData ? 0 : CurrentPage * PageSize,
        ShowAllData ? 0 : PageSize,
        ShowAllUserData,
        false,
        null,
        false,
        Filter,
        cancellationToken);
        if (ItemsProvider == null)
        {
            throw new Exception("ItemsProvider not set.");
        }
        var result = await ItemsProvider(req);
        Items?.Clear();
        Items?.AddRange(result.Items);
        //If pages count has changed.
        if (TotalCount != result.TotalItemCount)
        {
            TotalCount = result.TotalItemCount;
            Pages.Clear();
            if (!ShowAllData)
            {
                for (int pos = 0; pos != TotalPages; ++pos)
                {
                    Pages.Add(pos);
                }
            }
        }
        // Only apply result if the task was not canceled.
        if (!cancellationToken.IsCancellationRequested && renderOnSuccess)
        {
            StateHasChanged();
        }
    }
    protected override async Task OnInitializedAsync()
    {
        try
        {
            await RefreshDataAsync(false);
            if (ItemContent != null && Value != null)
            {
                Selected = ItemContent.Invoke(Value);
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex.Message);
        }
    }
}
